using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;

namespace Gko.Wpf.Mvvm
{
	/// <summary>
	///     This class allows delegating the commanding logic to methods passed as parameters,
	///     and enables a View to bind commands to objects that are not part of the element tree.
	/// </summary>
	public class DelegateCommand : ICommand
	{
		#region Constructors

		/// <summary>
		///     Constructor
		/// </summary>
		/// <exception cref="ArgumentNullException"><c>executeMethod</c> is null.</exception>
		public DelegateCommand(Action executeMethod)
			: this(executeMethod, null, false)
		{
		}

		/// <summary>
		///     Constructor
		/// </summary>
		/// <exception cref="ArgumentNullException"><c>executeMethod</c> is null.</exception>
		public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
			: this(executeMethod, canExecuteMethod, false)
		{
		}

		/// <summary>
		///     Constructor
		/// </summary>
		/// <exception cref="ArgumentNullException"><c>executeMethod</c> is null.</exception>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Requery")]
		public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
		{
			if (executeMethod == null) {
				throw new ArgumentNullException("executeMethod");
			}

			this.executeMethod = executeMethod;
			this.canExecuteMethod = canExecuteMethod;
			this.isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
		}

		#endregion

		#region Public Methods

		/// <summary>
		///     Property to enable or disable CommandManager's automatic requery on this command
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Requery")]
		public bool IsAutomaticRequeryDisabled
		{
			get
			{
				return this.isAutomaticRequeryDisabled;
			}
			set
			{
				if (this.isAutomaticRequeryDisabled == value) {
					return;
				}
				if (value) {
					CommandManagerHelper.RemoveHandlersFromRequerySuggested(this.canExecuteChangedHandlers);
				} else {
					CommandManagerHelper.AddHandlersToRequerySuggested(this.canExecuteChangedHandlers);
				}
				this.isAutomaticRequeryDisabled = value;
			}
		}

		/// <summary>
		///     Method to determine if the command can be executed
		/// </summary>
		public bool CanExecute()
		{
			return this.canExecuteMethod == null || this.canExecuteMethod();
		}

		/// <summary>
		///     Execution of the command
		/// </summary>
		public void Execute()
		{
			if (this.executeMethod != null) {
				this.executeMethod();
			}
		}

		/// <summary>
		///     Raises the CanExecuteChaged event
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public void RaiseCanExecuteChanged()
		{
			this.OnCanExecuteChanged();
		}

		/// <summary>
		///     Protected virtual method to raise CanExecuteChanged event
		/// </summary>
		protected virtual void OnCanExecuteChanged()
		{
			CommandManagerHelper.CallWeakReferenceHandlers(this.canExecuteChangedHandlers);
		}

		#endregion

		#region ICommand Members

		/// <summary>
		///     ICommand.CanExecuteChanged implementation
		/// </summary>
		public event EventHandler CanExecuteChanged
		{
			add
			{
				if (!this.isAutomaticRequeryDisabled) {
					CommandManager.RequerySuggested += value;
				}
				CommandManagerHelper.AddWeakReferenceHandler(ref this.canExecuteChangedHandlers, value, 2);
			}
			remove
			{
				if (!this.isAutomaticRequeryDisabled) {
					CommandManager.RequerySuggested -= value;
				}
				CommandManagerHelper.RemoveWeakReferenceHandler(this.canExecuteChangedHandlers, value);
			}
		}

		bool ICommand.CanExecute(object parameter)
		{
			return this.CanExecute();
		}

		void ICommand.Execute(object parameter)
		{
			this.Execute();
		}

		#endregion

		#region Data

		private readonly Func<bool> canExecuteMethod;
		private readonly Action executeMethod;
		private List<WeakReference> canExecuteChangedHandlers;
		private bool isAutomaticRequeryDisabled;

		#endregion
	}

	/// <summary>
	///     This class allows delegating the commanding logic to methods passed as parameters,
	///     and enables a View to bind commands to objects that are not part of the element tree.
	/// </summary>
	/// <typeparam name="T">Type of the parameter passed to the delegates</typeparam>
	public class DelegateCommand<T> : ICommand
	{
		#region Constructors

		/// <summary>
		///     Constructor
		/// </summary>
		/// <exception cref="ArgumentNullException"><c>executeMethod</c> is null.</exception>
		public DelegateCommand(Action<T> executeMethod)
			: this(executeMethod, null, false)
		{
		}

		/// <summary>
		///     Constructor
		/// </summary>
		/// <exception cref="ArgumentNullException"><c>executeMethod</c> is null.</exception>
		public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
			: this(executeMethod, canExecuteMethod, false)
		{
		}

		/// <summary>
		///     Constructor
		/// </summary>
		/// <exception cref="ArgumentNullException"><c>executeMethod</c> is null.</exception>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Requery")]
		public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
		{
			if (executeMethod == null) {
				throw new ArgumentNullException("executeMethod");
			}

			this.executeMethod = executeMethod;
			this.canExecuteMethod = canExecuteMethod;
			this.isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
		}

		#endregion

		#region Public Methods

		/// <summary>
		///     Property to enable or disable CommandManager's automatic requery on this command
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Requery")]
		public bool IsAutomaticRequeryDisabled
		{
			get
			{
				return this.isAutomaticRequeryDisabled;
			}
			set
			{
				if (this.isAutomaticRequeryDisabled == value) {
					return;
				}
				if (value) {
					CommandManagerHelper.RemoveHandlersFromRequerySuggested(this.canExecuteChangedHandlers);
				} else {
					CommandManagerHelper.AddHandlersToRequerySuggested(this.canExecuteChangedHandlers);
				}
				this.isAutomaticRequeryDisabled = value;
			}
		}

		/// <summary>
		///     Method to determine if the command can be executed
		/// </summary>
		public bool CanExecute(T parameter)
		{
			return this.canExecuteMethod == null || this.canExecuteMethod(parameter);
		}

		/// <summary>
		///     Execution of the command
		/// </summary>
		public void Execute(T parameter)
		{
			if (this.executeMethod != null) {
				this.executeMethod(parameter);
			}
		}

		/// <summary>
		///     Raises the CanExecuteChaged event
		/// </summary>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public void RaiseCanExecuteChanged()
		{
			this.OnCanExecuteChanged();
		}

		/// <summary>
		///     Protected virtual method to raise CanExecuteChanged event
		/// </summary>
		protected virtual void OnCanExecuteChanged()
		{
			CommandManagerHelper.CallWeakReferenceHandlers(this.canExecuteChangedHandlers);
		}

		#endregion

		#region ICommand Members

		/// <summary>
		///     ICommand.CanExecuteChanged implementation
		/// </summary>
		public event EventHandler CanExecuteChanged
		{
			add
			{
				if (!this.isAutomaticRequeryDisabled) {
					CommandManager.RequerySuggested += value;
				}
				CommandManagerHelper.AddWeakReferenceHandler(ref this.canExecuteChangedHandlers, value, 2);
			}
			remove
			{
				if (!this.isAutomaticRequeryDisabled) {
					CommandManager.RequerySuggested -= value;
				}
				CommandManagerHelper.RemoveWeakReferenceHandler(this.canExecuteChangedHandlers, value);
			}
		}

		bool ICommand.CanExecute(object parameter)
		{
			// if T is of value type and the parameter is not
			// set yet, then return false if CanExecute delegate
			// exists, else return true
			if (parameter == null && typeof(T).IsValueType) {
				return (this.canExecuteMethod == null);
			}
			return this.CanExecute((T)parameter);
		}

		void ICommand.Execute(object parameter)
		{
			this.Execute((T)parameter);
		}

		#endregion

		#region Data

		private readonly Func<T, bool> canExecuteMethod;
		private readonly Action<T> executeMethod;
		private List<WeakReference> canExecuteChangedHandlers;
		private bool isAutomaticRequeryDisabled;

		#endregion
	}
}